package work.base.jvm.summary;

/**
 * 1) 类加载过程包括   默认是懒加载 用到的时候再去加载
 * 1 加载  : class文件load到内存中 包括两部分内容
 *          （class文件的二进制内容和class对象，后者指向前者 通过class对象访问二进制的文件 执行字节码指令）
 *   jvm没有规定什么时候必须loading（所以一般是懒加载LazyLoading） 但是严格规定了什么时候必须初始化 几种情形
 *          初始化子类时 父类必定先初始化 new指定 反射调用时 虚拟机执行时 主类必定初始化等
 * 2 链接
 *      校验  检查class文件是否符合标准
 *      准备  静态变量赋默认值
 *      解析  class文件中的符号引用转化成直接引用 （内存地址） 对应loadClass 方法的第二个参数 是否解析
 * 3 初始化 ：static静态变量的赋初值 按代码顺序执行的
 * PS : new一个对象 对应字节码指令也是好几部分
 *      1 分配内存 成员变量(非static变量）赋默认值 2 调用构造方法 成员变量赋初始值
 *
 *
 * 2) 类加载器 （具体的实现和jvm的版本相关 openjdk和hotspot也不一样）
 * 四种类加载是双亲委派机制 自底向上检查
 * 每一个类都是被ClassLoader 加载到内存中的   父加载器(不是父类加载器)和父类继承不是一个概念
 *
 * 类加载器的文件在 rt.jar sun.misc.Launcher类文件中
 * 启动（Bootstrap）类加载器 ：由C++实现的  加载JAVA_HOME/lib下面的核心类库 lib/rt.jar charset.jar等核心类
 * 扩展（Extension）类加载器 : 加载扩展jar包     jre/lib/ext/*.jar ext包  或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中
 * 应用程序（APP）类加载器(系统类加载器)    ：classpath路径下的文件 一般是我们自己写的文件
 * 自定义类加载器 ：用户自定义
 *
 * 加载一个类的过程(双亲委派)： 先尝试在自定义的加载器进行查找 如果没有缓存 没加载过 (每个加载器都有自己的缓存）就往父类加载器寻找
 *              如果到Bootstrap还没有 就再从顶部往下进行加载 （按照每个加载器的规则来)  如果最后在自定义类加载器还没加载到 抛出异常ClassNotFound
 * 双亲委派机制的原因：主要为了安全 防止自定义的类覆盖掉核心类库
 * 打破双亲委派：重写loadClass()方法  场景是代码热替换(热加载)、模块热部署  tomcat的隔离性 两个web应用的类互不干涉
 *
 * 3) 自定义类加载器
 * AppClassLoader 和 ExtClassLoader 都扩展于 URLClassLoader加载器.
 * 两种方式 使用URLClassLoader类的loadClass()方法或者继承ClassLoader抽象类 重写findClass方法
 * 使用场景：
 *      Spring的动态代理类加载到内存
 *      热部署 不需要重启服务器
 *      加密class文件 防止反编译  class文件的字节与密钥做异或处理 然后写回文件 再异或相当于解密
 *
 * 4) 编译器
 *  jvm默认使用的是混合模式 混合使用编译器 + 热点代码解释器   可以通过jvm参数指定解释或者编译模式
 *  Java一般先是编译成class字节码文件 然后由jvm解释执行  (Java是解释型语言)
 *  因为编译之后的字节码文件不是机器可以直接识别的 所以java更多偏向是解释性语言
 *      JIT : 即时编译器 JIT会把热点代码编译成与本地平台相关的机器码(本地机器码) 之后就不需要再解释执行了
 *            如果全部都是编译执行 那么启动过程就很长
 *      热点代码检测 ：方法计数器 循环计数器
 *
 */
public class JvmSummary {

    public static void main(String[] args) {
        // 检查某个类是被哪个加载器加载到内存中的
        System.out.println(JvmSummary.class.getClassLoader());

        // getClassLoader为null 表示String这个类是由启动类加载器加载的
        System.out.println(String.class.getClassLoader());


        // 父加载器和类加载器的加载器不一样 也不是类加载器的父类加载器
        // AppClassLoader的加载器也是Bootstrap的 为null
        System.out.println(JvmSummary.class.getClassLoader().getClass().getClassLoader());
        System.out.println(JvmSummary.class.getClassLoader().getParent());

        // hotspot才可以执行
        // 可以看出哪些jar包是被我们的启动（Bootstrap）类加载器加载的
        String pathBoot = System.getProperty("sun.boot.class.path");
        System.out.println(pathBoot.replaceAll(";",System.lineSeparator()));

        // 可以看出哪些jar包是被我们的扩展（Extension）类加载器加载的
        System.out.println("======");
        String pathExt = System.getProperty("java.ext.dirs");
        System.out.println(pathExt.replaceAll(";",System.lineSeparator()));

        // 可以看出哪些jar包是被我们的应用程序（APP）类加载器加载的
        System.out.println("======");
        String pathApp = System.getProperty("java.class.path");
        System.out.println(pathApp.replaceAll(";",System.lineSeparator()));


    }


}
